package com.micro.ai.files.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.micro.ai.commons.domain.ApiResponse;
import com.micro.ai.commons.exception.BusinessException;
import com.micro.ai.files.entity.FileAttachment;
import com.micro.ai.files.service.FileAttachmentService;
import com.micro.ai.files.service.StorageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;

/**
 * 文件附件控制器
 * 提供文件上传、下载、管理功能
 * 
 * @author micro-ai
 * @since 0.0.1
 */
@Slf4j
@RestController
@RequestMapping("/api/file-attachments")
@Tag(name = "文件附件管理", description = "文件上传、下载、查询、删除等功能")
public class FileAttachmentController {

    @Autowired
    private FileAttachmentService fileAttachmentService;

    @Autowired
    private StorageService storageService;

    @Value("${file.default-storage-provider:minio}")
    private String defaultStorageProvider;

    @Value("${file.max-size:1073741824}")
    private Long maxFileSize;

    /**
     * 上传文件
     */
    @PostMapping("/upload")
    @PreAuthorize("hasAuthority('file:upload')")
    @Operation(summary = "上传文件", description = "上传单个文件，支持多种文件类型")
    public ApiResponse<FileAttachment> upload(
            @Parameter(description = "文件", required = true) @RequestParam("file") MultipartFile file,
            @Parameter(description = "租户ID") @RequestParam(value = "tenantId", required = false) String tenantId,
            @Parameter(description = "关联类型") @RequestParam(value = "associatedType", required = false) String associatedType,
            @Parameter(description = "关联ID") @RequestParam(value = "associatedId", required = false) String associatedId,
            @Parameter(description = "上传者ID") @RequestParam(value = "uploaderId", required = false) String uploaderId,
            @Parameter(description = "是否公开") @RequestParam(value = "isPublic", defaultValue = "false") Boolean isPublic,
            HttpServletRequest request) {
        
        if (file.isEmpty()) {
            return ApiResponse.error("F0000", "文件不能为空");
        }

        // 检查文件大小
        if (file.getSize() > maxFileSize) {
            return ApiResponse.error("F0001", "文件大小超过限制: " + (maxFileSize / 1024 / 1024) + "MB");
        }

        try {
            // 生成文件路径
            String datePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM"));
            String originalFilename = file.getOriginalFilename();
            String extension = "";
            if (originalFilename != null && originalFilename.contains(".")) {
                extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            }
            String storageFilename = UUID.randomUUID().toString().replace("-", "") + extension;
            String objectName = tenantId + "/" + datePath + "/" + storageFilename;

            // 读取文件内容到字节数组（用于计算哈希和上传）
            byte[] fileBytes = file.getBytes();
            
            // 计算文件哈希
            String fileHash = null;
            try {
                fileHash = calculateFileHash(fileBytes);
            } catch (Exception e) {
                log.warn("计算文件哈希失败，继续上传", e);
            }

            // 上传到MinIO（直接使用文件流）
            // 注意：由于已经读取了字节数组，我们需要重新创建输入流
            InputStream inputStream = new java.io.ByteArrayInputStream(fileBytes);
            String fileUrl = storageService.uploadFile(inputStream, objectName, file.getContentType(), file.getSize());

            // 判断文件类型
            String fileType = determineFileType(originalFilename, file.getContentType());

            // 创建文件附件记录
            FileAttachment attachment = new FileAttachment();
            attachment.setTenantId(tenantId);
            attachment.setFilename(storageFilename);
            attachment.setOriginalName(originalFilename);
            attachment.setFilePath(objectName); // 存储对象名称
            attachment.setFileSize(file.getSize());
            attachment.setFileHash(fileHash);
            attachment.setMimeType(file.getContentType());
            attachment.setFileType(fileType);
            attachment.setStorageProvider(defaultStorageProvider);
            attachment.setAssociatedType(associatedType);
            attachment.setAssociatedId(associatedId);
            attachment.setUploaderId(uploaderId);
            attachment.setIsPublic(isPublic != null && isPublic);
            attachment.setVersionCount(1);
            attachment.setReferenceCount(1);

            FileAttachment result = fileAttachmentService.create(attachment);

            log.info("文件上传成功: fileId={}, originalName={}, fileSize={}, objectName={}", 
                    result.getId(), originalFilename, file.getSize(), objectName);

            return ApiResponse.success("上传成功", result);

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("文件上传失败", e);
            throw new BusinessException("F0003", "文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 下载文件
     */
    @GetMapping("/{id}/download")
    @PreAuthorize("hasAuthority('file:download')")
    @Operation(summary = "下载文件", description = "根据文件ID下载文件")
    public ResponseEntity<Resource> download(
            @Parameter(description = "文件ID", required = true) @PathVariable String id,
            HttpServletRequest request) {
        
        FileAttachment attachment = fileAttachmentService.getById(id);
        if (attachment == null || attachment.getDeletedAt() != null) {
            return ResponseEntity.notFound().build();
        }

        try {
            // 从MinIO下载文件
            Resource resource = storageService.downloadFile(attachment.getFilePath());

            String contentType = attachment.getMimeType();
            if (contentType == null) {
                contentType = "application/octet-stream";
            }

            String filename = attachment.getOriginalName();
            if (filename == null) {
                filename = attachment.getFilename();
            }

            return ResponseEntity.ok()
                    .contentType(MediaType.parseMediaType(contentType))
                    .header(HttpHeaders.CONTENT_DISPOSITION, 
                            "attachment; filename=\"" + URLEncoder.encode(filename, StandardCharsets.UTF_8) + "\"")
                    .body(resource);

        } catch (Exception e) {
            log.error("文件下载失败: fileId={}", id, e);
            return ResponseEntity.internalServerError().build();
        }
    }

    /**
     * 获取文件信息（预览）
     */
    @GetMapping("/{id}")
    @PreAuthorize("hasAuthority('file:view')")
    @Operation(summary = "获取文件信息", description = "根据文件ID获取文件详细信息")
    public ApiResponse<FileAttachment> getById(
            @Parameter(description = "文件ID", required = true) @PathVariable String id) {
        
        FileAttachment attachment = fileAttachmentService.getById(id);
        if (attachment == null) {
            return ApiResponse.error("F0002", "文件不存在");
        }
        return ApiResponse.success(attachment);
    }

    /**
     * 分页查询文件
     */
    @GetMapping("/page")
    @PreAuthorize("hasAuthority('file:view')")
    @Operation(summary = "分页查询文件", description = "支持多条件查询文件列表")
    public ApiResponse<Page<FileAttachment>> pageQuery(
            @Parameter(description = "页码", example = "1") @RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
            @Parameter(description = "每页数量", example = "20") @RequestParam(value = "pageSize", defaultValue = "20") int pageSize,
            @Parameter(description = "租户ID") @RequestParam(value = "tenantId", required = false) String tenantId,
            @Parameter(description = "关键词") @RequestParam(value = "keyword", required = false) String keyword,
            @Parameter(description = "文件类型") @RequestParam(value = "fileType", required = false) String fileType,
            @Parameter(description = "关联类型") @RequestParam(value = "associatedType", required = false) String associatedType,
            @Parameter(description = "关联ID") @RequestParam(value = "associatedId", required = false) String associatedId) {
        
        Page<FileAttachment> page = fileAttachmentService.pageQuery(
                pageNum, pageSize, tenantId, keyword, fileType, associatedType, associatedId);
        return ApiResponse.success(page);
    }

    /**
     * 根据关联信息查询文件
     */
    @GetMapping("/by-associated")
    @PreAuthorize("hasAuthority('file:view')")
    @Operation(summary = "查询关联文件", description = "根据关联类型和ID查询文件列表")
    public ApiResponse<List<FileAttachment>> listByAssociated(
            @Parameter(description = "租户ID") @RequestParam(value = "tenantId", required = false) String tenantId,
            @Parameter(description = "关联类型", required = true) @RequestParam("associatedType") String associatedType,
            @Parameter(description = "关联ID", required = true) @RequestParam("associatedId") String associatedId) {
        
        List<FileAttachment> list = fileAttachmentService.listByAssociated(tenantId, associatedType, associatedId);
        return ApiResponse.success(list);
    }

    /**
     * 更新文件信息
     */
    @PutMapping("/{id}")
    @PreAuthorize("hasAuthority('file:update')")
    @Operation(summary = "更新文件信息", description = "更新文件的关联信息等")
    public ApiResponse<FileAttachment> update(
            @Parameter(description = "文件ID", required = true) @PathVariable String id,
            @Valid @RequestBody FileAttachment attachment) {
        
        FileAttachment result = fileAttachmentService.update(id, attachment);
        return ApiResponse.success("更新成功", result);
    }

    /**
     * 删除文件
     */
    @DeleteMapping("/{id}")
    @PreAuthorize("hasAuthority('file:delete')")
    @Operation(summary = "删除文件", description = "删除文件记录和物理文件")
    public ApiResponse<Void> delete(
            @Parameter(description = "文件ID", required = true) @PathVariable String id) {
        
        fileAttachmentService.delete(id);
        return ApiResponse.successVoid("删除成功");
    }

    /**
     * 批量删除关联文件
     */
    @DeleteMapping("/by-associated")
    @PreAuthorize("hasAuthority('file:delete')")
    @Operation(summary = "批量删除关联文件", description = "根据关联信息批量删除文件")
    public ApiResponse<Void> deleteByAssociated(
            @Parameter(description = "租户ID") @RequestParam(value = "tenantId", required = false) String tenantId,
            @Parameter(description = "关联类型", required = true) @RequestParam("associatedType") String associatedType,
            @Parameter(description = "关联ID", required = true) @RequestParam("associatedId") String associatedId) {
        
        fileAttachmentService.deleteByAssociated(tenantId, associatedType, associatedId);
        return ApiResponse.successVoid("删除成功");
    }

    /**
     * 计算文件哈希（SHA256）
     */
    private String calculateFileHash(byte[] fileBytes) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            digest.update(fileBytes);
            byte[] hashBytes = digest.digest();
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            log.warn("计算文件哈希失败", e);
            return null;
        }
    }

    /**
     * 判断文件类型
     */
    private String determineFileType(String filename, String contentType) {
        if (filename == null && contentType == null) {
            return "other";
        }

        String lowerFilename = filename != null ? filename.toLowerCase() : "";
        
        if (lowerFilename.endsWith(".jpg") || lowerFilename.endsWith(".jpeg") || 
            lowerFilename.endsWith(".png") || lowerFilename.endsWith(".gif") || 
            lowerFilename.endsWith(".bmp") || lowerFilename.endsWith(".webp") ||
            (contentType != null && contentType.startsWith("image/"))) {
            return "image";
        }
        
        if (lowerFilename.endsWith(".mp4") || lowerFilename.endsWith(".avi") || 
            lowerFilename.endsWith(".mov") || lowerFilename.endsWith(".wmv") ||
            (contentType != null && contentType.startsWith("video/"))) {
            return "video";
        }
        
        if (lowerFilename.endsWith(".mp3") || lowerFilename.endsWith(".wav") || 
            lowerFilename.endsWith(".flac") || lowerFilename.endsWith(".aac") ||
            (contentType != null && contentType.startsWith("audio/"))) {
            return "audio";
        }
        
        if (lowerFilename.endsWith(".zip") || lowerFilename.endsWith(".rar") || 
            lowerFilename.endsWith(".7z") || lowerFilename.endsWith(".tar") ||
            lowerFilename.endsWith(".gz")) {
            return "archive";
        }
        
        return "document";
    }
}

